// SPDX-License-Identifier: GPL-2.0
/******************************************************************************
 *    Copyright (c) 2009-2010 by  Hisilicon Tech. Co., Ltd.
 * ***
 *    written by CaiZhiYong 2010-09-17
 *
 ******************************************************************************/

#include <linux/module.h>
#include <linux/mtd/rawnand.h>
#include "hibase.h"
#include "hinand.h"
#include "hinfc301.h"
#include <linux/io.h>

#define _1K             (1024)
#define _2K             (2048)
#define _4K             (4096)
#define _8K             (8192)

#define _64K            (0x10000)
#define _128K           (0x20000)
#define _256K           (0x40000)
#define _512K           (0x80000)
#define _768K           (_256K + _512K)
#define _1M             (0x100000)
#define _2M             (_1M << 1)

#define _128M           (0x8000000UL)
#define _256M           (0x10000000UL)
#define _512M           (0x20000000UL)
#define _1G             (0x40000000ULL)
#define _2G             (0x80000000ULL)
#define _4G             (0x100000000ULL)
#define _8G             (0x200000000ULL)

#define HW_RET_FLASHSIZE_SUCCESS    0
#define HW_RET_FLASHSIZE_FAILED     1
#define HW_KER_FLASHSIZE_MAGIC      0xAA55A55A

#define NAND_SPI_IDS_TAG_NAND_ID    0x4E5F4944

#define SPI_CMD_READ                0x03   /* Read Data bytes */
#define SPI_CMD_READ_DUAL           0x3B   /* fast read dual output */
#define SPI_CMD_READ_QUAD           0x6B   /* fast read quad output */
#define SPI_CMD_WRITE_QUAD          0x32   /* fast program quad input */
#define SPI_CMD_PP                  0x02   /* Page Programming */

#ifndef __tagtable
#define __tagtable(tag, fn) \
static const struct tagtable __tagtable_##fn __used __section(".taglist.init") = { tag, fn }
#endif

static const char * const fmt[] = {"%uByte", "%uK", "%uM", "%uG", "%uT", "%uT"};

/*
 * Used to distinguish between mxu and ont, ont use the default value of 0,
 * mxu use a special value 0xff, mxu set in the uboot startup parameters
 */
int hi_nand_type;

struct nand_flash_special_dev {
	unsigned char id[8];
	int length;             /* length of id. */
	unsigned long long chipsize;
	struct nand_flash_dev *(*probe)(unsigned char id[8]);
	char *name;

	unsigned long pagesize;
	unsigned long erasesize;
	unsigned long oobsize;
	unsigned long options;
	struct op_code  opcode;
};


struct nand_flash_type {
	unsigned char id[8];
	unsigned int length;
};

/* this is nand id probe function. */
#define DRV_VERSION       "1.33"

static struct nand_flash_dev *hynix_probe_v02(unsigned char id[8])
{
	static struct nand_flash_dev type[2];

	int pagesizes[]   = {_2K, _4K, _8K, 0};
	int oobsizes[]    = {128, 224, 448, 0, 0, 0, 0, 0};
	int blocksizes[]  = {_128K, _256K, _512K, _768K, _1M, _2M, 0, 0};

	int blocktype = (((id[3] >> 5) & 0x04) | ((id[3] >> 4) & 0x03));
	int oobtype   = (((id[3] >> 2) & 0x03) | ((id[3] >> 4) & 0x04));

	type->options   = 0;
	type->pagesize  = pagesizes[(id[3] & 0x03)];
	type->erasesize = blocksizes[blocktype];
	*(unsigned long *)&type[1] = oobsizes[oobtype];

	return type;
}

static struct nand_flash_dev *samsung_probe_v02(unsigned char id[8])
{
	static struct nand_flash_dev type[2];

	int pagesizes[]   = {_2K, _4K, _8K, 0};
	int oobsizes[]    = {0, 128, 218, 400, 436, 0, 0, 0};
	int blocksizes[]  = {_128K, _256K, _512K, _1M, 0, 0, 0, 0};

	int blocktype = (((id[3] >> 5) & 0x04) | ((id[3] >> 4) & 0x03));
	int oobtype   = (((id[3] >> 4) & 0x04) | ((id[3] >> 2) & 0x03));

	type->options   = 0;
	type->pagesize  = pagesizes[(id[3] & 0x03)];
	type->erasesize = blocksizes[blocktype];
	*(unsigned long *)&type[1] = oobsizes[oobtype];

	return type;
}

static struct nand_flash_special_dev nand_flash_def = {
	.id = {0},
	.length = 0,
	.chipsize = _512M,
	/* .probe, */
	.name = "default_nand_flash",

	.pagesize = 0,
	.erasesize = 0,
	.oobsize = 0xFFFFFFFF,  /* resize oob size later. */
	/* .options, */
	.opcode.rd_code = {0x6b, 1, 3},
	.opcode.wr_code = {0x32, 0, 3},
	.opcode.erase_code = 0xd8,
};

static struct nand_flash_special_dev nand_flash_special_dev[] = {
	/*
	 * Micron
	 * 1st   2nd   3rd   4th   5th   6th   7th   8th   len  chipsize
	 * device      pagesize erasesize oobsize options
	 */
	{
		{0x2C, 0x48, 0x04, 0x46, 0x85, 0x00, 0x00, 0x00}, 8, _2G, NULL,
		"MT29F16G08CBABx", _4K,   _1M,      224,    0, { {0}, {0}, 0, {0} }
	}, {
		{0x2C, 0x48, 0x04, 0x4A, 0xA5, 0x00, 0x00, 0x00}, 8, _2G, NULL,
		"MT29F16G08CBACA", _4K,   _1M,      224,    0, { {0}, {0}, 0, {0} }
	}, {
		{0x2C, 0x38, 0x00, 0x26, 0x85, 0x00, 0x00, 0x00}, 8, _1G, NULL,
		"MT29F8G08ABxBAx", _4K,  _512K,     224,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0x88, 0x04, 0x4B, 0xA9, 0x00, 0x00, 0x00}, 8, _8G, NULL,
		"MT29F64G08CBAAA", _8K,   _2M,      448,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0x68, 0x04, 0x46, 0x89, 0x00, 0x00, 0x00}, 8, _8G, NULL,
		"MT29F64G08CBABA", _4K,   _1M,      224,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xF1, 0x80, 0x95, 0x04, 0x00, 0x00, 0x00}, 8, _128M, NULL,
		"MT29F1G08ABAEAWP:E", _2K,   _128K,      64,    0,
		{{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xF1, 0x80, 0x95, 0x04}, 5, _128M, NULL,
		"MT29F1G08ABAEA", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xA1, 0x80, 0x15, 0x04}, 5, _128M, NULL,
		"MT29F1G08ABBEA", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xB1, 0x80, 0x55, 0x04}, 5, _128M, NULL,
		"MT29F1G16ABBEA", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xDA, 0x90, 0x95, 0x06, 0x00, 0x00, 0x00}, 8, _256M, NULL,
		"MT29F2G08ABA", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xda, 0x90, 0x95, 0x06}, 5, _256M, NULL,
		"MT29F2G08ABAEAWP", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* BIWIN BW29F2G08ABAGA 8bit ECC */
		{0x2C, 0xDA, 0x90, 0x95, 0x86}, 5, _256M, NULL,
		"BW29F2G08ABAGA", _2K,  _128K,  128, 0, {{0}, {0}, 0, {0} }
	}, {
		/* MT29F8G08ABBC  */
		{0x2C, 0xA3, 0x90}, 3, _1G, NULL,
		"MT29F8G08ABBC", _4K, _256K,  224, 0,
		{{0}, {0}, 0, {0} }
	}, {
		/*
		 * Toshiba
		 * 1st   2nd   3rd   4th   5th   6th    len  chipsize   device
		 * pagesize erasesize oobsize options
		 */
		{0x98, 0xD5, 0x94, 0x32, 0x76, 0x55}, 6, _4G, NULL,
		"TC58NVG4D2F", _8K,     _1M,      448,     0, {{0}, {0}, 0, {0} }
	}, {
		{0x98, 0xD7, 0x94, 0x32, 0x76, 0x55}, 6, _8G, NULL,
		"TC58NVG6D2F", _8K,     _1M,      448,     0, {{0}, {0}, 0, {0} }
	}, {
		{0x98, 0xD1, 0x90, 0x15, 0x76, 0x14, }, 6, _128M, NULL,
		"TC58NVG0S3ETA00", _2K,     _128K,      64,     0,
		{{0}, {0}, 0, {0} }
	}, {
		/* TC58NVG2S3E */
		{0x98, 0xDC, 0x00, 0x11, 0x02}, 5, _4G, NULL,
		"TC58NVG2S3E", _2K,     _128K,      64,     0, {{0}, {0}, 0, {0} }
	}, {
		/* TC58NVG0S3ETAI0 */
		{0x98, 0xD1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00}, 8, _128M, NULL,
		"TC58NVG0S3ETAI0", _2K,     _128K,      64,     0,
		{{0}, {0}, 0, {0} }
	}, {
		/* TC58BVG0S3HTA00 */
		{0x98, 0xF1, 0x80, 0x15, 0xF2, 0x16}, 6, _128M, NULL,
		"TC58BVG0S3HTA00", _2K,     _128K,      64,     0,
		{{0}, {0}, 0, {0} }
	}, {
		{0x98, 0xDA, 0x90, 0x15, 0xF6}, 5, _256M, NULL,
		"TC58BVG1S3HTA00", _2K,     _128K,      64,     0,
		{{0}, {0}, 0, {0} }
	}, {
		/* Toshiba 8bit eccc */
		{0x98, 0xDA, 0x90, 0x15, 0x76}, 5, _256M, NULL,
		"TC58NVG1S3HTA00", _2K,     _128K,      128,     0,
		{{0}, {0}, 0, {0} }
	}, {
		/*
		 * Samsung
		 *     K9xxG08UxD: K9LBG08U0D / K9HCG08U1D / K9XDG08U5D
		 * 1st   2nd   3rd   4th   5th   6th     len
		 */
		{0xEC, 0xD7, 0xD5, 0x29, 0x38, 0x41}, 6, _4G, samsung_probe_v02,
		"K9xxG08UxD", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xEC, 0xD5, 0x84, 0x72, 0x50, 0x42}, 6, _2G, samsung_probe_v02,
		"K9GAG08U0E", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xEC, 0xD7, 0xC5, 0x72, 0x54, 0x42}, 6, _4G, samsung_probe_v02,
		"K9LBG08U0E", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xEC, 0xD3, 0x84, 0x72, 0x50, 0x42}, 6, _1G, samsung_probe_v02,
		"K9G8G08U0C", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * 1st   2nd   3rd   4th   5th     len  chipsize    device
		 * pagesize erasesize oobsize options
		 */
		{0xEC, 0xD7, 0x55, 0xB6, 0x78},  5,   _4G,  NULL,
		"K9xxG08UxM", _4K,     _512K,    128,    0, {{0}, {0}, 0, {0} }
	}, {
		/* K9F4G08U0D */
		{0xEC, 0xDC, 0x10, 0x95, 0x54},  5,   _4G,  NULL,
		"K9F4G08U0D", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* K9F1G08U0D */
		{0xEC, 0xF1, 0x00, 0x95, 0x40},  5,  _128M, NULL,
		"K9F1G08U0D", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Hynix
		 * 1st   2nd   3rd   4th   5th   6th     len
		 */
		{0xAD, 0xD5, 0x94, 0x25, 0x44, 0x41}, 6, _2G, hynix_probe_v02,
		"H27UAG8T2A", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xAD, 0xD5, 0x94, 0x9A, 0x74, 0x42}, 6, _2G, hynix_probe_v02,
		"H27UAG8T2B", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xAD, 0xD7, 0x94, 0x9A, 0x74, 0x42}, 6, _4G, hynix_probe_v02,
		"H27UBG8T2A", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * 1st   2nd   3rd   4th   5th   len chipsize   device
		 * pagesize erasesize oobsize options
		 */
		/* HY27UF084G2B */
		{0xAD, 0xDC, 0x10, 0x95, 0x54}, 5, _4G, NULL,
		"H27UF084G2B", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* H27U1G8F2CTR */
		{0xAD, 0xF1, 0x80, 0x1D},       4, _128M, NULL,
		"H27U1G8F2CTR", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* H27U2G8F2CTR */
		{0xAD, 0xDA, 0x90, 0x95, 0x44},       5, _256M, NULL,
		"H27U2G8F2CTR-BI", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Spansion
		 * 1st   2nd   3rd   4th   5th   6th   len
		 */
		/* S34ML01G100TF100 */
		{0x01, 0xF1, 0x00, 0x1D, 0x01, 0xF1, 0x00, 0x1D}, 8, _128M, NULL,
		"S34ML01G100TF100", _2K,     _128K,      64,     0,
		{{0}, {0}, 0, {0} }
	}, {
		{0x01, 0xF1, 0x00, 0x1D, 0x01},  4,   _128M,  NULL,
		"S34ML01G1", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x01, 0xDA, 0x90, 0x95, 0x44, 0x01, 0xDA, 0x90}, 8, _256M, NULL,
		"S34ML02G1", _2K,  _128K,  64, 0, {{0}, {0}, 0, {0} }
	}, {
		{0x01, 0xda, 0x90, 0x95, 0x44},  5,   _256M,  NULL,
		"S34ML02G100X8", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x01, 0xCa, 0x90, 0xD5, 0x44},  5,   _256M,  NULL,
		"S34ML02G100X16", _1K, _64K,    32,    0, {{0}, {0}, 0, {0} }
	}, {
		/* S34ML02G200TF100 */
		{0x01, 0xDA, 0x90, 0x95, 0x46, 0x01, 0xDA, 0x90}, 8, _256M, NULL,
		"S34ML02G2", _2K,  _128K,  128, 0, {{0}, {0}, 0, {0} }
	}, {
		/* S34ML02G200TF000 */
		{0x01, 0xDA, 0x90, 0x95, 0x46},  5,   _256M,  NULL,
		"S34ML02G200TFI000&1", _2K,  _128K,  128, 0, {{0}, {0}, 0, {0} }
	}, {
		/* Spansion S34ML01G2 */
		{0x01, 0xF1, 0x80, 0x1D}, 4, _128M, NULL,
		"S34ML01G2", _2K,  _128K,  64, 0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Zentel
		 * 1st   2nd   3rd   4th   5th   len
		 */
		{0x92, 0xF1, 0x80, 0x95, 0x40},  5,   _128M,  NULL,
		"A5U1GA31ATS", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Winbond
		 * 1st   2nd   3rd   4th   5th   len
		 */
		{0xEF, 0xF1, 0x80, 0x95, 0x00},  5,   _128M,  NULL,
		"W29N01", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0xEF, 0xDA, 0x90, 0x95, 0x04},  5,   _256M,  NULL,
		"W29N02GV", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Macronix
		 * 1st   2nd   3rd   4th   5th   len
		 */
		{0xC2, 0xF1, 0x80, 0x95, 0x02},  5,   _128M,  NULL,
		"MX30LF1G18AC", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0xC2, 0xDA, 0x90, 0x95, 0x06},  5,   _256M,  NULL,
		"MX30LF2G18AC", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* Powerchip Technology PSU2GA30BT-G4IA */
		{0xC8, 0xDA, 0x90, 0x95, 0x44},  5,   _256M, NULL,
		"PSU2GA30BT-G4IA", _2K,  _128K,  128, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xC8, 0xDA, 0x90, 0x95, 0x46},  5,   _256M, NULL,
		"PSU2GA30BT-G1IA", _2K,  _128K,  64, 0, {{0}, {0}, 0, {0} }
	}, {
		/* GD9FU1G8F2A */
		{0xC8, 0xF1, 0x80, 0x1D, 0x42}, 5, _128M, NULL,
		"GD9FU1G8F2A", _2K,  _128K,  128, 0, {{0}, {0}, 0, {0} }
	}, {
		/* F59L2G81A */
		{0xC8, 0xDA, 0x90, 0x95, 0x44}, 5, _256M, NULL,
		"F59L2G81A", _2K,  _128K,  64, 0, {{0}, {0}, 0, {0} }
	}, {
		/* Dosilicon FMND2G08U3D-IA (4bit ECC) */
		{0xF8, 0xF1, 0x80, 0x95}, 4, _128M, NULL,
		"FMND1G08U3D-IA", _2K,  _128K,  64, 0, {{0}, {0}, 0, {0} }
	}, {
		/* Dosilicon DSND8G08S3N (4bit ECC) */
		{0xE5, 0xA3, 0xC1}, 3, _1G, NULL,
		"DSND8G08S3N", _4K,  _256K,  256, 0,
		{{0}, {0}, 0, {0} }
	}, {
		{0xF8, 0xDA, 0x90, 0x95, 0x46}, 5, _256M, NULL,
		"FMND2G08U3D-IA", _2K,  _128K,  64, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xE5, 0x71},  2,   _128M, NULL,
		"DS35Q1GA-IB", _2K, _128K, 64, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0xE5, 0x72},  2,   _256M, NULL,
		"DS35Q2GA-IB", _2K, _128K, 64, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	},  {
		{0xE5, 0xF1},  2,   _128M, NULL,
		"DS35Q1G", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		/* spi nand flash   mode 4 io */
		{0xEF, 0xAA, 0x21},  3,   _128M, NULL,
		"W25N01GV", _2K, _128K, 64, 0, {{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0xEF, 0xAB, 0x21},  3,   _256M, NULL,
		"W25M02GV", _2K, _128K, 64, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {2, 0xc2} }
	}, {
		{0xC2, 0x12},  2,   _128M, NULL,
		"MX35LF1GE4AB", _2K, _128K, 64, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0xC2, 0x20, 0xC2},  3,   _256M, NULL,
		"MX35LF2G14AC", _2K, _128K, 64, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0x2C, 0x24},  2,   _256M, NULL,
		"MT29F2G01ABAGD12ITES:F", _2K, _128K, 64, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0x2C, 0x34},  2, _512M, NULL,
		"MT29F4G01ADAGD", _4K, _256K, 256, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		/* Toshiba (8bit ECC) */
		{0x98, 0xC2},  2,   _128M, NULL,
		"TC58CVG0S3HRAIG", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x02, 0, 0}, 0xd8, {0} }
	}, {
		{0x98, 0xCB},  2,   _256M, NULL,
		"TC58CVG1S3HRAIG", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x02, 0, 0}, 0xd8, {0} }
	}, {
		{0x98, 0xCD},  2, _512M, NULL,
		"TC58CVG1S3HRAIG", _4K, _256K, 256, 0,
		{{0x6b, 1, 3}, {0x02, 0, 0}, 0xd8, {0} }
	}, {
		{0xC8, 0x92},  2, _256M, NULL,
		"GD5F2GM7UE", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x02, 0, 0}, 0xd8, {0} }
	}, {
		/* TH58NYG3S0HBAI4 */
		{0x98, 0xA3, 0x91}, 3, _1G, NULL,
		"TH58NYG3S0HBAI4", _4K, _256K, 256, 0,
		{{0}, {0}, 0, {0} }
	}, {
		/* GigaDevice (8bit ECC) */
		{0xC8, 0xD1},  2,   _128M, NULL,
		"GD5F1GQ4UEYIG", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0xC8, 0xD2},  2,   _256M, NULL,
		"GD5F2GQ4UB9IG", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0xC8, 0x91},  2,   _128M, NULL,
		"HUYANG030",     _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0xA1, 0xEC}, 2, _128M, NULL,
		"HUYANG074", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0xA1, 0xD7}, 2, _128M, NULL,
		"HUYANG074", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0xA1, 0xD6}, 2, _256M, NULL,
		"HUYANG074", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0xA1, 0xE5}, 2, _256M, NULL,
		"HUYANG074", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0xC2, 0x24, 0x03}, 3, _256M, NULL,
		"MX35LF1G24AD", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0x0B, 0x12}, 2, _256M, NULL,
		"HUYANG073", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0xE5, 0xF2}, 2, _256M, NULL,
		"HUYANG035", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0xE5, 0x51}, 2, _128M, NULL,
		"HUYANG035", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0}}
	}, {
		{0}, 0, 0, 0, 0, 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	},
};

static struct nand_flash_special_dev nand_flash_special_dev_mxu[] = {
	/*
	 * Micron
	 * 1st   2nd   3rd   4th   5th   6th   7th   8th   len  chipsize
	 * device      pagesize erasesize oobsize options
	 */
	{
		{0x2C, 0x48, 0x04, 0x46, 0x85, 0x00, 0x00, 0x00}, 8, _2G, NULL,
		"MT29F16G08CBABx", _4K,   _1M,      224,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0x48, 0x04, 0x4A, 0xA5, 0x00, 0x00, 0x00}, 8, _2G, NULL,
		"MT29F16G08CBACA", _4K,   _1M,      224,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0x38, 0x00, 0x26, 0x85, 0x00, 0x00, 0x00}, 8, _1G, NULL,
		"MT29F8G08ABxBAx", _4K,  _512K,     224,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0x88, 0x04, 0x4B, 0xA9, 0x00, 0x00, 0x00}, 8, _8G, NULL,
		"MT29F64G08CBAAA", _8K,   _2M,      448,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0x68, 0x04, 0x46, 0x89, 0x00, 0x00, 0x00}, 8, _8G, NULL,
		"MT29F64G08CBABA", _4K,   _1M,      224,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xF1, 0x80, 0x95, 0x04, 0x00, 0x00, 0x00}, 8, _128M, NULL,
		"MT29F1G08ABAEAWP:E", _2K,   _128K,      64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xF1, 0x80, 0x95, 0x04}, 5, _128M, NULL,
		"MT29F1G08ABAEA", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xA1, 0x80, 0x15, 0x04}, 5, _128M, NULL,
		"MT29F1G08ABBEA", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xB1, 0x80, 0x55, 0x04}, 5, _128M, NULL,
		"MT29F1G16ABBEA", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xDA, 0x90, 0x95, 0x06, 0x00, 0x00, 0x00}, 8, _256M, NULL,
		"MT29F2G08ABA", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x2C, 0xda, 0x90, 0x95, 0x06}, 5, _256M, NULL,
		"MT29F2G08ABAEAWP", _2K,   _128K,     64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Toshiba
		 * 1st   2nd   3rd   4th   5th   6th    len  chipsize   device
		 * pagesize erasesize oobsize options
		 */
		{0x98, 0xD5, 0x94, 0x32, 0x76, 0x55}, 6, _4G, NULL,
		"TC58NVG4D2F", _8K,     _1M,      448,     0, {{0}, {0}, 0, {0} }
	}, {
		{0x98, 0xD7, 0x94, 0x32, 0x76, 0x55}, 6, _8G, NULL,
		"TC58NVG6D2F", _8K,     _1M,      448,     0, {{0}, {0}, 0, {0} }
	}, {
		{0x98, 0xD1, 0x90, 0x15, 0x76, 0x14, }, 6, _128M, NULL,
		"TC58NVG0S3ETA00", _2K,     _128K,      64,     0, {{0}, {0}, 0, {0} }
	}, {
		/* TC58NVG2S3E */
		{0x98, 0xDC, 0x00, 0x11, 0x02}, 5, _4G, NULL,
		"TC58NVG2S3E", _2K,     _128K,      64,     0, {{0}, {0}, 0, {0} }
	}, {
		/* TC58NVG2S3ETAI0 */
		{0x98, 0xDC, 0x90, 0x15, 0x76, 0x14, 0x03, 0x10}, 8, _512M, NULL,
		"TC58NVG2S3ETAI0", _2K,     _128K,      64,     0, {{0}, {0}, 0, {0} }
	}, {
		/* TC58NVG0S3ETAI0 */
		{0x98, 0xD1, 0x90, 0x15, 0x76, 0x14, 0x01, 0x00}, 8, _128M, NULL,
		"TC58NVG0S3ETAI0", _2K,     _128K,      64,     0, {{0}, {0}, 0, {0} }
	}, {
		/* TC58BVG0S3HTA00 */
		{0x98, 0xF1, 0x80, 0x15, 0xF2, 0x16}, 6, _128M, NULL,
		"TC58BVG0S3HTA00", _2K,     _128K,      64,     0, {{0}, {0}, 0, {0} }
	}, {
		/* TH58NYG3S0HBAI4 */
		{0x98, 0xA3, 0x91}, 3, _1G, NULL,
		"TH58NYG3S0HBAI4", _4K,     _256K,      256,     0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Samsung
		 *     K9xxG08UxD: K9LBG08U0D / K9HCG08U1D / K9XDG08U5D
		 * 1st   2nd   3rd   4th   5th   6th     len
		 */
		{0xEC, 0xD7, 0xD5, 0x29, 0x38, 0x41}, 6, _4G, samsung_probe_v02,
		"K9xxG08UxD", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xEC, 0xD5, 0x84, 0x72, 0x50, 0x42}, 6, _2G, samsung_probe_v02,
		"K9GAG08U0E", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xEC, 0xD7, 0xC5, 0x72, 0x54, 0x42}, 6, _4G, samsung_probe_v02,
		"K9LBG08U0E", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xEC, 0xD3, 0x84, 0x72, 0x50, 0x42}, 6, _1G, samsung_probe_v02,
		"K9G8G08U0C", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * 1st   2nd   3rd   4th   5th     len  chipsize    device
		 * pagesize erasesize oobsize options
		 */
		{0xEC, 0xD7, 0x55, 0xB6, 0x78},  5,   _4G,  NULL,
		"K9xxG08UxM", _4K,     _512K,    128,    0, {{0}, {0}, 0, {0} }
	}, {
		/* K9F4G08U0D */
		{0xEC, 0xDC, 0x10, 0x95, 0x54},  5,   _4G,  NULL,
		"K9F4G08U0D", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* K9F1G08U0D */
		{0xEC, 0xF1, 0x00, 0x95, 0x40},  5,  _128M, NULL,
		"K9F1G08U0D", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Hynix
		 * 1st   2nd   3rd   4th   5th   6th     len
		 */
		{0xAD, 0xD5, 0x94, 0x25, 0x44, 0x41}, 6, _2G, hynix_probe_v02,
		"H27UAG8T2A", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xAD, 0xD5, 0x94, 0x9A, 0x74, 0x42}, 6, _2G, hynix_probe_v02,
		"H27UAG8T2B", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		{0xAD, 0xD7, 0x94, 0x9A, 0x74, 0x42}, 6, _4G, hynix_probe_v02,
		"H27UBG8T2A", 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * 1st   2nd   3rd   4th   5th   len chipsize   device
		 * pagesize erasesize oobsize options
		 */
		/* HY27UF084G2B */
		{0xAD, 0xDC, 0x10, 0x95, 0x54}, 5, _4G, NULL,
		"H27UF084G2B", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* H27U1G8F2CTR */
		{0xAD, 0xF1, 0x80, 0x1D},       4, _128M, NULL,
		"H27U1G8F2CTR", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* H27U2G8F2CTR */
		{0xAD, 0xDA, 0x90, 0x95, 0x44},       5, _256M, NULL,
		"H27U2G8F2CTR-BI", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* H27U4G8F2DTR */
		{0xAD, 0xDC, 0x90, 0x95, 0x54}, 5, _512M, NULL,
		"H27U4G8F2DTR", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Spansion
		 * 1st   2nd   3rd   4th   5th   6th   len
		 */
		/* S34ML01G100TF100 */
		{0x01, 0xF1, 0x00, 0x1D, 0x01, 0xF1, 0x00, 0x1D}, 8, _128M, NULL,
		"S34ML01G100TF100", _2K,     _128K,      64,     0,
		{{0}, {0}, 0, {0} }
	}, {
		{0x01, 0xF1, 0x00, 0x1D, 0x01},  4,   _128M,  NULL,
		"S34ML01G1", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x01, 0xDA, 0x90, 0x95, 0x44, 0x01, 0xDA, 0x90}, 8, _256M, NULL,
		"S34ML02G1", _2K,  _128K,  64, 0, {{0}, {0}, 0, {0} }
	}, {
		{0x01, 0xda, 0x90, 0x95, 0x44},  5,   _256M,  NULL,
		"S34ML02G100X8", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0x01, 0xCa, 0x90, 0xD5, 0x44},  5,   _256M,  NULL,
		"S34ML02G100X16", _1K, _64K,    32,    0, {{0}, {0}, 0, {0} }
	}, {
		/* S34ML02G200TF100 */
		{0x01, 0xDA, 0x90, 0x95, 0x46, 0x01, 0xDA, 0x90}, 8, _256M, NULL,
		"S34ML02G2", _2K,  _128K,  128, 0, {{0}, {0}, 0, {0} }
	}, {
		/* S34ML02G200TF000 */
		{0x01, 0xDA, 0x90, 0x95, 0x46},  5,   _256M,  NULL,
		"S34ML02G200TFI000&1", _2K,  _128K,  128, 0, {{0}, {0}, 0, {0} }
	}, {
		/* Spansion S34ML01G2 */
		{0x01, 0xF1, 0x80, 0x1D}, 4, _128M, NULL,
		"S34ML01G2", _2K,  _128K,  64, 0, {{0}, {0}, 0, {0} }
	}, {
		/* S34ML04G100TFI */
		{0x01, 0xDC, 0x90, 0x95, 0x54}, 5, _512M, NULL,
		"S34ML04G100TFI", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Zentel
		 * 1st   2nd   3rd   4th   5th   len
		 */
		{0x92, 0xF1, 0x80, 0x95, 0x40},  5,   _128M,  NULL,
		"A5U1GA31ATS", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Winbond
		 * 1st   2nd   3rd   4th   5th   len
		 */
		{0xEF, 0xF1, 0x80, 0x95, 0x00},  5,   _128M,  NULL,
		"W29N01", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0xEF, 0xDA, 0x90, 0x95, 0x04},  5,   _256M,  NULL,
		"W29N02GV", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/*
		 * Macronix
		 * 1st   2nd   3rd   4th   5th   len
		 */
		{0xC2, 0xF1, 0x80, 0x95, 0x02},  5,   _128M,  NULL,
		"MX30LF1G18AC", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		{0xC2, 0xDA, 0x90, 0x95, 0x06},  5,   _256M,  NULL,
		"MX30LF2G18AC", _2K,     _128K,    64,    0, {{0}, {0}, 0, {0} }
	}, {
		/* spi nand flash   mode 4 io */
		{0xEF, 0xAA, 0x21},  3,   _128M, NULL,
		"W25N01GV", _2K, _128K, 64, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0xC2, 0x12},  2,   _128M, NULL,
		"MX35LF1GE4AB", _2K, _128K, 64, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0xC2, 0x14, 0x03},  3,   _128M, NULL,
		"MX35LF1G24AD-Z4IH", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0x0B, 0x11},  2,   _128M, NULL,
		"XT26G01C", _2K, _128K, 128, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0x2C, 0x34},  2, _512M, NULL,
		"MT29F4G01ADAGD", _4K, _256K, 256, 0,
		{{0x6b, 1, 3}, {0x32, 0, 3}, 0xd8, {0} }
	}, {
		{0x98, 0xCD},  2, _512M, NULL,
		"TC58CVG1S3HRAIG", _4K, _256K, 256, 0,
		{{0x6b, 1, 3}, {0x02, 0, 0}, 0xd8, {0} }
	}, {
		/* Longsys electronics */
		{ 0xCD, 0xF1, 0x00, 0x95, 0x40 }, 5, _128M, NULL,
		"FSNS8A001G", _2K,  _128K,  64, 0, { {0}, {0}, 0, {0} }
	}, {
		{0}, 0, 0, 0, 0, 0, 0, 0, 0, {{0}, {0}, 0, {0} }
	},
};

#define NUM_OF_SPECIAL_DEVICE  \
(sizeof(nand_flash_special_dev)/sizeof(struct nand_flash_special_dev))

#define NUM_OF_SPECIAL_DEVICE_MXU  \
(sizeof(nand_flash_special_dev_mxu)/sizeof(struct nand_flash_special_dev))

struct nand_flash_type g_st_nand_flash_id = {{0}, 0};
EXPORT_SYMBOL(g_st_nand_flash_id);

unsigned int flashsize;
static int __init cmdline_flashsize_setup(char *str)
{
	ssize_t ret;

	if (!str)
		return 0;

	ret = kstrtou32(str, 0, &flashsize);
	if (ret)
		return 0;
	return 1;
}
__setup("flashsize=", cmdline_flashsize_setup);

struct nand_flash_dev *nand_get_special_flash_type(unsigned char id[8], struct nand_chip *chip)
{
	struct nand_flash_dev *flash_type = NULL;
	struct nand_flash_special_dev *spl_dev = NULL;
	struct hinfc_host *host = chip->priv;

	pr_info("Nand ID: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
		id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]);

	if (strstr(g_flash_cmd_line, "flashsize=0x10000000") != NULL)
		flashsize = 0x10000000;
	else if (strstr(g_flash_cmd_line, "flashsize=0x20000000") != NULL)
		flashsize = 0x20000000;
	else
		flashsize = 0x8000000;

	/* 0 ont ; 0xff mxu */
	if (hi_nand_type != 0xff)
		spl_dev = nand_flash_special_dev;
	else
		spl_dev = nand_flash_special_dev_mxu;

	for (; spl_dev->length; spl_dev++) {
		if (memcmp(id, spl_dev->id, spl_dev->length) == 0)
			break;
	}

	/* use default NAND Flash config */
	if (spl_dev->length == 0) {
		nand_flash_def.pagesize = host->hw_pagesize;
		nand_flash_def.erasesize = host->hw_blocksize;

		pr_info("now using default nand flash type,Block:%ldKB Page:%ldKB!\n",
			nand_flash_def.erasesize / 1024, nand_flash_def.pagesize / 1024);
		switch (flashsize) {
		/* go through cases of 8M, 16M, 32M, 256M, 512M */
		case 0x800000:
		case 0x1000000:
		case 0x2000000:
		case 0x10000000:
		case 0x20000000:
			break;
		/* set to 128M if "flashsize=" not found or out of range [8M,512M] */
		default:
			flashsize = 0x8000000;
			break;
		}
		nand_flash_def.chipsize = (unsigned long long)flashsize;
		pr_info("Default flash solution!\r\n");
		spl_dev = &nand_flash_def;
	}

	memcpy(g_st_nand_flash_id.id, spl_dev->id, spl_dev->length);
	g_st_nand_flash_id.length = spl_dev->length;

	if (spl_dev->probe)	{
		flash_type = spl_dev->probe(id);
	} else {
		static struct nand_flash_dev type[2];

		type->options   = spl_dev->options;
		type->pagesize  = spl_dev->pagesize;
		type->erasesize = spl_dev->erasesize;
		*(unsigned long *)&type[1] = spl_dev->oobsize;
		g_default_oob_size = spl_dev->oobsize;

		flash_type = type;
	}

	if (host->flashtype == SPI_NAND_FLASH_TYPE) {
		memcpy(&host->opcode, &spl_dev->opcode, sizeof(struct op_code));

		/*
		 * default is x4 mode,can use bootargs to config
		 * flash_io_mode=read:x4,write:x4
		 */
		if (strstr(g_flash_cmd_line, "read:x4") != NULL) {
			host->opcode.rd_code.opcode = SPI_CMD_READ_QUAD;
			host->opcode.rd_code.if_type = 3;
		} else if (strstr(g_flash_cmd_line, "read:x2") != NULL) {
			host->opcode.rd_code.opcode = SPI_CMD_READ_DUAL;
			host->opcode.rd_code.if_type = 1;
		} else if (strstr(g_flash_cmd_line, "read:x1") != NULL) {
			host->opcode.rd_code.opcode = SPI_CMD_READ;
			host->opcode.rd_code.if_type = 0;
		}

		if (strstr(g_flash_cmd_line, "write:x4") != NULL) {
			host->opcode.wr_code.opcode = SPI_CMD_WRITE_QUAD;
			host->opcode.wr_code.if_type = 3;
		} else if (strstr(g_flash_cmd_line, "write:x2") != NULL) {
			host->opcode.wr_code.opcode = SPI_CMD_PP;
			host->opcode.wr_code.if_type = 1;
		} else if (strstr(g_flash_cmd_line, "write:x1") != NULL) {
			host->opcode.wr_code.opcode = SPI_CMD_PP;
			host->opcode.wr_code.if_type = 0;
		}

		pr_info("rd_code.opcode %u, if_type = %u, wr_code.opcode %u, if_type = %u\r\n",
				host->opcode.rd_code.opcode,
				host->opcode.rd_code.if_type,
				host->opcode.wr_code.opcode,
				host->opcode.wr_code.if_type);
	}

	memcpy(flash_type->id, id, NAND_MAX_ID_LEN);
	flash_type->id_len = spl_dev->length;
	flash_type->chipsize = (unsigned long)(spl_dev->chipsize >> 20);
	flash_type->name     = spl_dev->name;

	return flash_type;
}

struct nand_tag {
	unsigned char id[8];
	int length;
	unsigned long long chipsize;
	unsigned long pagesize;
	unsigned long erasesize;
	unsigned long oobsize;
	unsigned long options;
	char name[16];
};

static char *to_size_string(unsigned long long size)
{
	int i;
	static char buffer[20];

	/* loop for 5 times */
	for (i = 0; (i < 5) && (size >= 1024); i++)
		size = (size >> 10);

	sprintf(buffer, fmt[i], size);
	return buffer;
}

static int __init parse_nand_id(const struct tag *tag)
{
	static char chipname[16];
	struct nand_tag *nandtag = NULL;
	struct nand_flash_special_dev *spldev = NULL;

	/* 0 ont ; 0xff mxu */
	if (hi_nand_type != 0xff)
		spldev = nand_flash_special_dev + NUM_OF_SPECIAL_DEVICE - 2;
	else
		spldev = nand_flash_special_dev_mxu + NUM_OF_SPECIAL_DEVICE_MXU - 2;

	if (tag->hdr.size != ((sizeof(struct tag_header) + sizeof(struct nand_tag)) >> 2)) {
		pr_warn("%s(%d):	tag->hdr.size(%d) too small.\n",
				__func__, __LINE__, tag->hdr.size);
		return 0;
	}

	nandtag = (struct nand_tag *)&tag->u;

	strncpy(chipname, nandtag->name, sizeof(nandtag->name));
	chipname[sizeof(chipname) - 1] = '\0';

	memcpy(spldev->id, nandtag->id, sizeof(nandtag->id));
	spldev->length = nandtag->length;
	spldev->chipsize = nandtag->chipsize;
	spldev->probe = NULL;
	spldev->name = chipname;

	spldev->pagesize = nandtag->pagesize;
	spldev->erasesize = nandtag->erasesize;
	spldev->oobsize = nandtag->oobsize;
	spldev->options = nandtag->options;

	pr_info("NAND TAG: hdr.tag: 0x%08X, hdr.size: %d\n", tag->hdr.tag, tag->hdr.size);
	pr_info("(%dByte): 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
		nandtag->length, nandtag->id[0], nandtag->id[1], nandtag->id[2], nandtag->id[3],
		nandtag->id[4], nandtag->id[5], nandtag->id[6], nandtag->id[7]);
	pr_info("Block:%s ",    to_size_string(nandtag->erasesize));
	pr_info("Page:%s ",     to_size_string(nandtag->pagesize));
	pr_info("Chip:%s ",     to_size_string(nandtag->chipsize));
	pr_info("OOB:%s ",      to_size_string(nandtag->oobsize));
	pr_info("Opt:0x%08lX ", nandtag->options);
	pr_info("Name:(%s)",    nandtag->name);
	pr_info("\n");

	return 0;
}

/* turn to ascii is "N_ID" */
__tagtable(NAND_SPI_IDS_TAG_NAND_ID, parse_nand_id);

void nand_spl_ids_init(void)
{
	pr_info("Special nand id table Version %s\n", DRV_VERSION);
	nand_base_get_special_flash_type = nand_get_special_flash_type;
}

MODULE_IMPORT_NS(HW_RTOS_NS);
